#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/bitrev.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/blkdev.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/uio.h>
#include <linux/idr.h>
#include <linux/bsg.h>
#include <linux/slab.h>

#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include <linux/spinlock.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>

#define NAME "ramdisk"
//一个扇区大小。最难搞清的参数。。。
#define SECTOR_SIZE 512
//3MB大小
#define SIZE_BYTE (3*1024*1024)  
//3个分区
#define AREA  3

struct ramdisk
{
    int major;
    void *buffer;
    spinlock_t lock;
    struct gendisk *gendisk;
    struct request_queue *queue;
};

static struct ramdisk disk;

static int ramdisk_open(struct block_device *bdev, fmode_t mode)
{
    printk("open\r\n");
    return 0;
}
static void ramdisk_release(struct gendisk *disk, fmode_t mode)
{
    printk("release\r\n");
}   
static int ramdisk_getgeo(struct block_device * device, struct hd_geometry *geo)
{ 
    /* 这是相对于机械硬盘的概念 */ 
    geo->heads = 2; /* 磁头 */ 
    geo->cylinders = 32; /* 柱面 */ 
    geo->sectors = SIZE_BYTE / (2 * 32 *SECTOR_SIZE); /* 磁道上的扇区数量 */ 
    return 0;  
}
static const struct block_device_operations ramdisk_fops =
{
	.owner		= THIS_MODULE,
	.open		= ramdisk_open,
	.release	= ramdisk_release,
    .getgeo = ramdisk_getgeo,
};
static void do_request(struct request_queue *q)
{
	struct request *req;
    int err = 0;
	req = blk_fetch_request(q);
    while(req!=NULL)
    {
        void *buffer = bio_data(req->bio);//内存起始地址
        unsigned long start = blk_rq_pos(req) *SECTOR_SIZE; /* 扇区地址转换为字节地址 ,在内核眼里硬盘从0-MAX，实际上是从buff开始的模拟*/ 
        unsigned long len = blk_rq_cur_bytes(req); /* 大小 */
        if (rq_data_dir(req) == READ)
            memcpy(buffer, disk.buffer+start, len);//物理设备如果是1024字节就自己指定
        else
            memcpy(disk.buffer+start, buffer, len);
        if (!__blk_end_request_cur(req, err))
            req = blk_fetch_request(q);
    }
}

static int __init ramdisk_init(void)
{
//1、注册块设备
//2、申请具体设备
//3、初始化具体设备（操作集和请求队列）,请求队列可选择是否需要磁盘调度算法
//4、注册具体设备到内核
    disk.buffer = kmalloc(SIZE_BYTE,GFP_KERNEL);

    disk.major= register_blkdev(0,NAME);
    if(disk.major<0)
        goto ERROR;
    disk.gendisk = alloc_disk(AREA);
    if(IS_ERR(disk.gendisk))
        goto FREE_register_blkdev;

    spin_lock_init(&disk.lock);

    disk.queue = blk_init_queue(do_request, &disk.lock);
    if(IS_ERR(disk.queue))
        goto FREE_alloc_disk;

    disk.gendisk->major = disk.major;
    disk.gendisk->first_minor = 0;
    disk.gendisk->fops = &ramdisk_fops;
    sprintf(disk.gendisk->disk_name, NAME);
    disk.gendisk->queue = disk.queue;
    set_capacity(disk.gendisk,SIZE_BYTE/512);

    add_disk(disk.gendisk);

    return 0;


FREE_alloc_disk:
    del_gendisk(disk.gendisk);
    put_disk(disk.gendisk);
FREE_register_blkdev:
    unregister_blkdev(disk.major,NAME);
ERROR:
    kfree(disk.buffer);
    return -1;
}
static void __exit ramdisk_exit(void)
{
    blk_cleanup_queue(disk.queue); 
    del_gendisk(disk.gendisk);
    put_disk(disk.gendisk);
    unregister_blkdev(disk.major,NAME);
    kfree(disk.buffer);
}
module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_ALIAS("xiao");
MODULE_LICENSE("GPL");
